/** * Frontend JavaScript - assets/js/frontend.js */ (function($) { 'use strict'; // Variável global para armazenar as instâncias do tracker window.lgtTrackers = {}; /** * Inicializar um tracker específico */ window.lgtInitTracker = function(trackerId) { const $tracker = $('#' + trackerId); if (!$tracker.length) { console.error('Tracker not found:', trackerId); return; } // Criar nova instância do tracker const tracker = new LGTTracker($tracker); // Armazenar instância window.lgtTrackers[trackerId] = tracker; // Inicializar tracker.init(); }; /** * Classe do Tracker */ class LGTTracker { /** * Construtor */ constructor($tracker) { this.$tracker = $tracker; this.trackerId = $tracker.attr('id'); this.map = null; this.mapMarkers = []; this.geocoder = null; this.placesService = null; this.lastResults = null; this.currentScanId = null; } /** * Inicializar o tracker */ init() { // Elementos DOM this.$businessName = this.$tracker.find('.lgt-business-name'); this.$searchQuery = this.$tracker.find('.lgt-search-query'); this.$businessAddress = this.$tracker.find('.lgt-business-address'); this.$gridSize = this.$tracker.find('.lgt-grid-size'); this.$radius = this.$tracker.find('.lgt-radius'); this.$unit = this.$tracker.find('.lgt-unit'); this.$unitButtons = this.$tracker.find('.lgt-unit-button'); this.$runScanButton = this.$tracker.find('.lgt-run-scan'); this.$resultsContainer = this.$tracker.find('.lgt-results'); this.$resultsMap = this.$tracker.find('.lgt-results-map'); this.$resultsDetails = this.$tracker.find('.lgt-results-details'); this.$loading = this.$tracker.find('.lgt-loading'); this.$errorMessage = this.$tracker.find('.lgt-error-message'); this.$addToMonitoring = this.$tracker.find('.lgt-add-to-monitoring'); this.$exportResults = this.$tracker.find('.lgt-export-results'); // Inicializar serviços do Google Maps this.initGoogleMapsServices(); // Adicionar event listeners this.addEventListeners(); } /** * Inicializar serviços do Google Maps */ initGoogleMapsServices() { // Verificar se a API do Google Maps está carregada if (typeof google === 'undefined' || typeof google.maps === 'undefined') { console.error('Google Maps API not loaded'); return; } // Inicializar geocoder this.geocoder = new google.maps.Geocoder(); // Inicializar autocompletar para endereço const autocomplete = new google.maps.places.Autocomplete( this.$businessAddress[0], { types: ['address'] } ); // Adicionar listener para o autocompletar autocomplete.addListener('place_changed', () => { const place = autocomplete.getPlace(); if (!place.geometry) { return; } // Se o endereço tiver um estabelecimento, preencher o nome do negócio if (place.name && place.types && (place.types.includes('establishment') || place.types.includes('point_of_interest'))) { this.$businessName.val(place.name); } }); } /** * Adicionar event listeners */ addEventListeners() { // Botões de unidade (KM/Miles) this.$unitButtons.on('click', (e) => { const $button = $(e.currentTarget); const unit = $button.data('unit'); // Atualizar botões this.$unitButtons.removeClass('active'); $button.addClass('active'); // Atualizar campo oculto this.$unit.val(unit); }); // Botão de execução do scan this.$runScanButton.on('click', () => { this.runScan(); }); // Botão de adicionar ao monitoramento this.$addToMonitoring.on('click', () => { this.addToMonitoring(); }); // Botão de exportar resultados this.$exportResults.on('click', () => { this.exportResults(); }); } /** * Executar o scan */ runScan() { // Obter valores dos campos const businessName = this.$businessName.val(); const searchQuery = this.$searchQuery.val(); const businessAddress = this.$businessAddress.val(); const gridSize = this.$gridSize.val(); const radius = this.$radius.val(); const unit = this.$unit.val(); // Validar campos if (!searchQuery) { this.showError('Please enter a search query.'); return; } if (!businessAddress) { this.showError('Please enter a business address.'); return; } // Mostrar loading this.$loading.show(); this.$resultsContainer.hide(); this.$errorMessage.hide(); // Fazer requisição Ajax $.ajax({ url: lgtData.ajaxurl, type: 'POST', data: { action: 'lgt_run_scan', nonce: lgtData.nonce, business_name: businessName, search_query: searchQuery, business_address: businessAddress, grid_size: gridSize, radius: radius, unit: unit }, success: (response) => { if (response.success) { this.lastResults = response.data; this.currentScanId = response.data.scan_id; this.displayResults(response.data); } else { this.showError(response.data.message || 'An error occurred while running the scan.'); } }, error: () => { this.showError('An error occurred while communicating with the server.'); }, complete: () => { this.$loading.hide(); } }); } /** * Mostrar erro */ showError(message) { this.$loading.hide(); this.$errorMessage.text(message).show(); } /** * Mostrar resultados */ displayResults(data) { // Exibir container de resultados this.$resultsContainer.show(); // Inicializar o mapa this.initResultsMap(data); // Exibir detalhes this.displayResultDetails(data); } /** * Inicializar mapa de resultados */ initResultsMap(data) { // Verificar se a API do Google Maps está carregada if (typeof google === 'undefined' || typeof google.maps === 'undefined') { console.error('Google Maps API not loaded'); return; } // Limpar marcadores existentes if (this.mapMarkers.length) { this.mapMarkers.forEach(marker => marker.setMap(null)); this.mapMarkers = []; } // Criar mapa se não existir if (!this.map) { this.map = new google.maps.Map(this.$resultsMap[0], { center: { lat: data.center.lat, lng: data.center.lng }, zoom: 14, mapTypeId: google.maps.MapTypeId.ROADMAP, mapTypeControl: true, mapTypeControlOptions: { style: google.maps.MapTypeControlStyle.HORIZONTAL_BAR, position: google.maps.ControlPosition.TOP_RIGHT }, fullscreenControl: true, streetViewControl: false }); } else { // Atualizar centro do mapa this.map.setCenter({ lat: data.center.lat, lng: data.center.lng }); } // Adicionar marcador do negócio const businessMarker = new google.maps.Marker({ position: { lat: data.center.lat, lng: data.center.lng }, map: this.map, title: data.business_name || 'Business Location', icon: { path: google.maps.SymbolPath.CIRCLE, scale: 10, fillColor: '#4285F4', fillOpacity: 1, strokeColor: '#FFFFFF', strokeWeight: 2 } }); this.mapMarkers.push(businessMarker); // Adicionar marcadores da grade this.addGridMarkersToMap(data); } /** * Adicionar marcadores da grade ao mapa */ addGridMarkersToMap(data) { // Iterar pelas linhas e colunas da grade for (let rowIndex = 0; rowIndex < data.grid_results.length; rowIndex++) { const row = data.grid_results[rowIndex]; for (let colIndex = 0; colIndex < row.length; colIndex++) { const cell = row[colIndex]; if (!cell || cell.error) continue; // Determinar a posição do negócio nesta célula let position = 'none'; let businessFound = false; // Se temos um nome de negócio, buscar nas posições if (data.business_name) { // Procurar nos resultados desta célula if (cell.results && cell.results.length) { for (let i = 0; i < cell.results.length; i++) { const result = cell.results[i]; if (result.name.toLowerCase().includes(data.business_name.toLowerCase())) { position = result.position; businessFound = true; break; } } } } // Criar elemento HTML para o marcador de grade const markerElement = document.createElement('div'); markerElement.className = `lgt-grid-cell lgt-position-${businessFound ? position : 'none'}`; markerElement.textContent = businessFound ? position : '0'; // Criar overlay personalizado const gridMarker = new GridMarker( new google.maps.LatLng(cell.lat, cell.lng), this.map, { content: markerElement } ); // Adicionar ao array de marcadores this.mapMarkers.push(gridMarker); } } } /** * Exibir detalhes dos resultados */ displayResultDetails(data) { let detailsHtml = `

Scan Results Summary

Business: ${data.business_name || 'N/A'}

Search Query: ${data.search_query}

Grid Size: ${data.grid_size}x${data.grid_size}

Radius: ${data.radius} ${data.unit}

`; // Adicionar análise de posicionamento se temos um nome de negócio if (data.business_name && data.business_positions && data.business_positions.length) { // Calcular estatísticas let positionCounts = {}; let totalPositions = data.business_positions.length; let bestPosition = 100; let worstPosition = 0; let averagePosition = 0; data.business_positions.forEach(pos => { const position = pos.position; positionCounts[position] = (positionCounts[position] || 0) + 1; bestPosition = Math.min(bestPosition, position); worstPosition = Math.max(worstPosition, position); averagePosition += position; }); if (totalPositions > 0) { averagePosition = (averagePosition / totalPositions).toFixed(1); } detailsHtml += `

Position Analysis

Your business "${data.business_name}" appears in ${totalPositions} out of ${data.grid_size * data.grid_size} grid cells.

Average Position: ${averagePosition}

Best Position: ${bestPosition === 100 ? 'Not found' : bestPosition}

Worst Position: ${worstPosition === 0 ? 'Not found' : worstPosition}

Position Distribution

Recommendations

`; } else { detailsHtml += `

No business name provided for position analysis. Enter your business name and run the scan again to see detailed position analysis.

`; } this.$resultsDetails.html(detailsHtml); } /** * Adicionar ao monitoramento */ addToMonitoring() { // Verificar se temos resultados if (!this.lastResults) { this.showError('No scan results available. Please run a scan first.'); return; } // Verificar se temos nome de negócio if (!this.lastResults.business_name) { this.showError('Business name is required for monitoring. Please enter a business name and run the scan again.'); return; } // Confirmar com o usuário if (!confirm('Do you want to add this business to monitoring? This will create a recurring scan with the current settings.')) { return; } // Fazer requisição Ajax $.ajax({ url: lgtData.ajaxurl, type: 'POST', data: { action: 'lgt_add_to_monitoring', nonce: lgtData.nonce, business_name: this.lastResults.business_name, search_query: this.lastResults.search_query, grid_size: this.lastResults.grid_size, radius: this.lastResults.radius, center_lat: this.lastResults.center.lat, center_lng: this.lastResults.center.lng, frequency: 'weekly' }, success: (response) => { if (response.success) { alert('Added to monitoring successfully! You can manage your monitoring from the admin dashboard.'); } else { this.showError(response.data.message || 'An error occurred while adding to monitoring.'); } }, error: () => { this.showError('An error occurred while communicating with the server.'); } }); } /** * Exportar resultados */ exportResults() { // Verificar se temos resultados if (!this.lastResults) { this.showError('No scan results available. Please run a scan first.'); return; } // Formatar data const now = new Date(); const timestamp = now.toISOString().replace(/[:.]/g, '-'); // Preparar conteúdo do relatório const reportContent = JSON.stringify(this.lastResults, null, 2); // Criar um blob const blob = new Blob([reportContent], { type: 'application/json' }); // Criar URL para o blob const url = URL.createObjectURL(blob); // Criar link de download const a = document.createElement('a'); a.href = url; a.download = `lgt-report-${timestamp}.json`; // Adicionar à página, clicar e remover document.body.appendChild(a); a.click(); document.body.removeChild(a); // Liberar URL setTimeout(() => { URL.revokeObjectURL(url); }, 100); } } /** * Classe de marcador de grade personalizado */ class GridMarker extends google.maps.OverlayView { /** * Construtor */ constructor(position, map, options = {}) { super(); this.position = position; this.options = options; this.setMap(map); } /** * Inicializar marcador */ onAdd() { this.div = document.createElement('div'); this.div.style.position = 'absolute'; if (this.options.content) { this.div.appendChild(this.options.content); } // Adicionar à camada de sobreposições do mapa const panes = this.getPanes(); panes.overlayMouseTarget.appendChild(this.div); // Adicionar listener de clique google.maps.event.addDomListener(this.div, 'click', (e) => { google.maps.event.trigger(this, 'click'); e.stopPropagation(); }); } /** * Desenhar marcador */ draw() { // Transformar coordenadas para pixels const overlayProjection = this.getProjection(); const position = overlayProjection.fromLatLngToDivPixel(this.position); // Posicionar elemento this.div.style.left = position.x + 'px'; this.div.style.top = position.y + 'px'; } /** * Remover marcador */ onRemove() { if (this.div) { this.div.parentNode.removeChild(this.div); this.div = null; } } /** * Definir mapa */ setMap(map) { if (this.div) { this.onRemove(); } super.setMap(map); } } })(jQuery); /** * Admin JavaScript - assets/js/admin.js */ (function($) { 'use strict'; // Quando o documento estiver pronto $(document).ready(function() { // Inicializar componentes específicos baseados na página atual switch (lgtAdminData.current_page) { case 'toplevel_page_local-grid-tracker': initDashboard(); break; case 'local-grid-tracker_page_local-grid-tracker-settings': initSettings(); break; case 'local-grid-tracker_page_local-grid-tracker-history': initHistory(); break; case 'local-grid-tracker_page_local-grid-tracker-monitoring': initMonitoring(); break; } }); /** * Inicializar página de dashboard */ function initDashboard() { // Por enquanto, não há funcionalidade específica para o dashboard } /** * Inicializar página de configurações */ function initSettings() { // Teste de API key $('#lgt-test-api-key').on('click', function() { const apiKey = $('#lgt_google_api_key').val(); const $resultContainer = $('#lgt-api-key-test-result'); if (!apiKey) { $resultContainer.removeClass('success').addClass('error').text('Please enter an API key.'); return; } // Mostrar loading $resultContainer.removeClass('success error').text('Testing API key...'); // Fazer requisição Ajax $.ajax({ url: lgtAdminData.ajaxurl, type: 'POST', data: { action: 'lgt_test_api_key', nonce: lgtAdminData.nonce, api_key: apiKey }, success: function(response) { if (response.success) { $resultContainer.removeClass('error').addClass('success').text(response.data.message); } else { $resultContainer.removeClass('success').addClass('error').text(response.data.message); } }, error: function() { $resultContainer.removeClass('success').addClass('error').text('An error occurred while testing the API key.'); } }); }); } /** * Inicializar página de histórico */ function initHistory() { // Verificar se a API do Google Maps está carregada if (typeof google === 'undefined' || typeof google.maps === 'undefined') { console.error('Google Maps API not loaded'); return; } let map = null; let mapMarkers = []; let currentScanId = null; let lastResults = null; // Inicializar autocomplete para o endereço const autocomplete = new google.maps.places.Autocomplete( document.getElementById('lgt-business-address'), { types: ['address'] } ); // Adicionar listener para o autocomplete autocomplete.addListener('place_changed', function() { const place = autocomplete.getPlace(); if (!place.geometry) { return; } // Se o endereço tiver um estabelecimento, preencher o nome do negócio if (place.name && place.types && (place.types.includes('establishment') || place.types.includes('point_of_interest'))) { $('#lgt-business-name').val(place.name); } }); // Carregar histórico de buscas loadSearchHistory(); // Adicionar listener para o botão de scan $('#lgt-run-scan').on('click', function() { runScan(); }); // Adicionar listener para botões de monitoramento e exportação $('#lgt-add-to-monitoring').on('click', function() { addToMonitoring(); }); $('#lgt-export-results').on('click', function() { exportResults(); }); /** * Carregar histórico de buscas */ function loadSearchHistory(page = 1, limit = 10) { // Fazer requisição Ajax $.ajax({ url: lgtAdminData.ajaxurl, type: 'POST', data: { action: 'lgt_get_search_history', nonce: lgtAdminData.nonce, page: page, limit: limit }, success: function(response) { if (response.success) { displaySearchHistory(response.data.history); } else { alert(response.data.message || 'An error occurred while loading search history.'); } }, error: function() { alert('An error occurred while communicating with the server.'); } }); } /** * Exibir histórico de buscas */ function displaySearchHistory(history) { const $tableBody = $('#lgt-history-table-body'); $tableBody.empty(); if (!history || !history.length) { $tableBody.html('No search history found.'); return; } // Adicionar cada registro à tabela history.forEach(function(record) { const recordDate = new Date(record.date_created); const formattedDate = recordDate.toLocaleString(); const $row = $(` ${record.business_name || 'N/A'} ${record.search_query} ${record.grid_size}x${record.grid_size} ${record.radius} ${formattedDate} `); $tableBody.append($row); }); // Adicionar listener para os botões de visualização $('.lgt-view-scan').on('click', function() { const scanId = $(this).data('id'); loadScanDetails(scanId); }); } /** * Carregar detalhes de um scan */ function loadScanDetails(scanId) { // Fazer requisição Ajax $.ajax({ url: lgtAdminData.ajaxurl, type: 'POST', data: { action: 'lgt_get_scan_details', nonce: lgtAdminData.nonce, scan_id: scanId }, success: function(response) { if (response.success) { currentScanId = scanId; const scanData = response.data.scan_data; // Transformar os dados da grade para o formato esperado const formattedData = { scan_id: scanId, business_name: scanData.business_name, search_query: scanData.search_query, grid_size: scanData.grid_size, radius: scanData.radius, unit: 'km', // Assumir km por padrão center: { lat: parseFloat(scanData.center_lat), lng: parseFloat(scanData.center_lng) }, grid_results: scanData.grid_data }; lastResults = formattedData; displayScanResults(formattedData); } else { alert(response.data.message || 'An error occurred while loading scan details.'); } }, error: function() { alert('An error occurred while communicating with the server.'); } }); } /** * Executar scan */ function runScan() { // Obter valores dos campos const businessName = $('#lgt-business-name').val(); const searchQuery = $('#lgt-search-query').val(); const businessAddress = $('#lgt-business-address').val(); const gridSize = $('#lgt-grid-size').val(); const radius = $('#lgt-radius').val(); const unit = $('#lgt-unit').val(); // Validar campos if (!searchQuery) { alert('Please enter a search query.'); return; } if (!businessAddress) { alert('Please enter a business address.'); return; } // Mostrar loading $('#lgt-scan-status').text('Running scan...'); // Fazer requisição Ajax $.ajax({ url: lgtAdminData.ajaxurl, type: 'POST', data: { action: 'lgt_run_scan', nonce: lgtAdminData.nonce, business_name: businessName, search_query: searchQuery, business_address: businessAddress, grid_size: gridSize, radius: radius, unit: unit }, success: function(response) { if (response.success) { currentScanId = response.data.scan_id; lastResults = response.data; displayScanResults(response.data); // Recarregar histórico loadSearchHistory(); } else { alert(response.data.message || 'An error occurred while running the scan.'); } }, error: function() { alert('An error occurred while communicating with the server.'); }, complete: function() { $('#lgt-scan-status').text(''); } }); } /** * Exibir resultados do scan */ function displayScanResults(data) { // Exibir container de resultados $('#lgt-scan-results').show(); // Inicializar mapa initResultsMap(data); // Exibir detalhes displayResultDetails(data); } /** * Inicializar mapa de resultados */ function initResultsMap(data) { // Limpar marcadores existentes if (mapMarkers.length) { mapMarkers.forEach(function(marker) { marker.setMap(null); }); mapMarkers = []; } // Criar mapa se não existir if (!map) { map = new google.maps.Map(document.getElementById('lgt-results-map'), { center: { lat: data.center.lat, lng: data.center.lng }, zoom: 14, mapTypeId: google.maps.MapTypeId.ROADMAP, mapTypeControl: true, mapTypeControlOptions: { style: google.maps.MapTypeControlStyle.HORIZONTAL_BAR, position: google.maps.ControlPosition.TOP_RIGHT }, fullscreenControl: true, streetViewControl: false }); } else { // Atualizar centro do mapa map.setCenter({ lat: data.center.lat, lng: data.center.lng }); } // Adicionar marcador do negócio const businessMarker = new google.maps.Marker({ position: { lat: data.center.lat, lng: data.center.lng }, map: map, title: data.business_name || 'Business Location', icon: { path: google.maps.SymbolPath.CIRCLE, scale: 10, fillColor: '#4285F4', fillOpacity: 1, strokeColor: '#FFFFFF', strokeWeight: 2 } }); mapMarkers.push(businessMarker); // Adicionar marcadores da grade addGridMarkersToMap(data); } /** * Adicionar marcadores da grade ao mapa */ function addGridMarkersToMap(data) { // Iterar pelas linhas e colunas da grade for (let rowIndex = 0; rowIndex < data.grid_results.length; rowIndex++) { const row = data.grid_results[rowIndex]; for (let colIndex = 0; colIndex < row.length; colIndex++) { const cell = row[colIndex]; if (!cell || cell.error) continue; // Determinar a posição do negócio nesta célula let position = 'none'; let businessFound = false; // Se temos um nome de negócio, buscar nas posições if (data.business_name) { // Procurar nos resultados desta célula if (cell.results && cell.results.length) { for (let i = 0; i < cell.results.length; i++) { const result = cell.results[i]; if (result.name.toLowerCase().includes(data.business_name.toLowerCase())) { position = result.position; businessFound = true; break; } } } } // Criar elemento HTML para o marcador de grade const markerElement = document.createElement('div'); markerElement.className = `lgt-grid-cell lgt-position-${businessFound ? position : 'none'}`; markerElement.textContent = businessFound ? position : '0'; // Criar overlay personalizado const gridMarker = new GridMarker( new google.maps.LatLng(cell.lat, cell.lng), map, { content: markerElement } ); // Adicionar ao array de marcadores mapMarkers.push(gridMarker); } } } /** * Exibir detalhes dos resultados */ function displayResultDetails(data) { const $detailsContainer = $('#lgt-results-details'); let detailsHtml = `

Scan Results Summary

Business: ${data.business_name || 'N/A'}

Search Query: ${data.search_query}

Grid Size: ${data.grid_size}x${data.grid_size}

Radius: ${data.radius} ${data.unit || 'km'}

`; $detailsContainer.html(detailsHtml); } /** * Adicionar ao monitoramento */ function addToMonitoring() { // Verificar se temos resultados if (!lastResults) { alert('No scan results available. Please run a scan first.'); return; } // Verificar se temos nome de negócio if (!lastResults.business_name) { alert('Business name is required for monitoring. Please enter a business name and run the scan again.'); return; } // Confirmar com o usuário if (!confirm('Do you want to add this business to monitoring? This will create a recurring scan with the current settings.')) { return; } // Fazer requisição Ajax $.ajax({ url: lgtAdminData.ajaxurl, type: 'POST', data: { action: 'lgt_add_to_monitoring', nonce: lgtAdminData.nonce, business_name: lastResults.business_name, search_query: lastResults.search_query, grid_size: lastResults.grid_size, radius: lastResults.radius, center_lat: lastResults.center.lat, center_lng: lastResults.center.lng, frequency: 'weekly' }, success: function(response) { if (response.success) { alert('Added to monitoring successfully!'); } else { alert(response.data.message || 'An error occurred while adding to monitoring.'); } }, error: function() { alert('An error occurred while communicating with the server.'); } }); } /** * Exportar resultados */ function exportResults() { // Verificar se temos resultados if (!lastResults) { alert('No scan results available. Please run a scan first.'); return; } // Formatar data const now = new Date(); const timestamp = now.toISOString().replace(/[:.]/g, '-'); // Preparar conteúdo do relatório const reportContent = JSON.stringify(lastResults, null, 2); // Criar um blob const blob = new Blob([reportContent], { type: 'application/json' }); // Criar URL para o blob const url = URL.createObjectURL(blob); // Criar link de download const a = document.createElement('a'); a.href = url; a.download = `lgt-report-${timestamp}.json`; // Adicionar à página, clicar e remover document.body.appendChild(a); a.click(); document.body.removeChild(a); // Liberar URL setTimeout(function() { URL.revokeObjectURL(url); }, 100); } } /** * Inicializar página de monitoramento */ function initMonitoring() { // Carregar monitoramentos loadMonitoring(); /** * Carregar monitoramentos */ function loadMonitoring() { // Fazer requisição Ajax $.ajax({ url: lgtAdminData.ajaxurl, type: 'POST', data: { action: 'lgt_get_monitoring', nonce: lgtAdminData.nonce }, success: function(response) { if (response.success) { displayMonitoring(response.data.monitoring); } else { alert(response.data.message || 'An error occurred while loading monitoring data.'); } }, error: function() { alert('An error occurred while communicating with the server.'); } }); } /** * Exibir monitoramentos */ function displayMonitoring(monitoring) { const $tableBody = $('#lgt-monitoring-table-body'); $tableBody.empty(); if (!monitoring || !monitoring.length) { $tableBody.html('No active monitoring found.'); return; } // Adicionar cada registro à tabela monitoring.forEach(function(record) { const lastRun = record.last_run ? new Date(record.last_run).toLocaleString() : 'Never'; const nextRun = record.next_run ? new Date(record.next_run).toLocaleString() : 'N/A'; const $row = $(` ${record.business_name} ${record.search_query} ${record.grid_size}x${record.grid_size} ${record.radius} ${record.frequency} ${lastRun} ${nextRun} `); $tableBody.append($row); }); // Adicionar listeners $('.lgt-monitoring-frequency').on('change', function() { const id = $(this).data('id'); const frequency = $(this).val(); updateMonitoring(id, { frequency: frequency }); }); $('.lgt-toggle-monitoring').on('click', function() { const id = $(this).data('id'); const currentEnabled = $(this).data('enabled'); const newEnabled = !currentEnabled; updateMonitoring(id, { enabled: newEnabled }); }); } /** * Atualizar monitoramento */ function updateMonitoring(id, data) { // Fazer requisição Ajax $.ajax({ url: lgtAdminData.ajaxurl, type: 'POST', data: { action: 'lgt_update_monitoring', nonce: lgtAdminData.nonce, id: id, ...data }, success: function(response) { if (response.success) { // Recarregar monitoramentos loadMonitoring(); } else { alert(response.data.message || 'An error occurred while updating monitoring.'); } }, error: function() { alert('An error occurred while communicating with the server.'); } }); } } /** * Classe de marcador de grade personalizado */ class GridMarker extends google.maps.OverlayView { /** * Construtor */ constructor(position, map, options = {}) { super(); this.position = position; this.options = options; this.setMap(map); } /** * Inicializar marcador */ onAdd() { this.div = document.createElement('div'); this.div.style.position = 'absolute'; if (this.options.content) { this.div.appendChild(this.options.content); } // Adicionar à camada de sobreposições do mapa const panes = this.getPanes(); panes.overlayMouseTarget.appendChild(this.div); // Adicionar listener de clique google.maps.event.addDomListener(this.div, 'click', (e) => { google.maps.event.trigger(this, 'click'); e.stopPropagation(); }); } /** * Desenhar marcador */ draw() { // Transformar coordenadas para pixels const overlayProjection = this.getProjection(); const position = overlayProjection.fromLatLngToDivPixel(this.position); // Posicionar elemento this.div.style.left = position.x + 'px'; this.div.style.top = position.y + 'px'; } /** * Remover marcador */ onRemove() { if (this.div) { this.div.parentNode.removeChild(this.div); this.div = null; } } /** * Definir mapa */ setMap(map) { if (this.div) { this.onRemove(); } super.setMap(map); } } })(jQuery);